package com.mikufufu.blog.utils;

import lombok.extern.slf4j.Slf4j;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.FileNameMap;
import java.net.URLConnection;
import java.nio.file.Files;
import java.text.DecimalFormat;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.text.DecimalFormatSymbols;
import java.util.Locale;

/**
 * 文件工具类
 * 用于文件操作
 * 比如文件大小转换等
 *
 */
@Slf4j
public class FileUtils {

    private static final FileNameMap mimeMap = URLConnection.getFileNameMap();

    /**
     * 使用ThreadLocal来存储每个线程独有的DecimalFormat实例，避免多线程下的并发问题
     */
    private static final ThreadLocal<DecimalFormat> DECIMAL_FORMAT_THREAD_LOCAL = ThreadLocal.withInitial(() -> {
        // 初始化DecimalFormat，使用美国 Locale 以确保小数分隔符为点
        DecimalFormat decimalFormat = new DecimalFormat("#.##", new DecimalFormatSymbols(Locale.US));
        // 设置不使用千位分隔符
        decimalFormat.setGroupingUsed(false);
        return decimalFormat;
    });

    /**
     * 将字节大小转换为更易读的文件大小格式
     * 此方法根据文件大小选择合适的单位（B，KB，MB，GB），并格式化为两位小数
     * 注意：此方法不支持超过1TB的文件大小转换
     *
     * @param size 字节大小
     * @return {@link String} 转换后的文件大小
     * @throws IllegalArgumentException 如果文件大小超过1TB
     */
    public static String convertFileSize(long size) {
        // 检查文件大小是否超过1TB，如果超过则抛出异常
        if (size > 1024L * 1024 * 1024 * 1024) {
            throw new IllegalArgumentException("文件超过1TB");
        }

        // 获取当前线程的DecimalFormat实例
        DecimalFormat decimalFormat = DECIMAL_FORMAT_THREAD_LOCAL.get();

        // 根据文件大小，选择合适的单位进行转换
        if (size < 1024) {
            return size + "B";
        } else if (size < 1024 * 1024) {
            return decimalFormat.format(size / 1024.0) + "KB";
        } else if (size < 1024 * 1024 * 1024) {
            return decimalFormat.format(size / (1024.0 * 1024)) + "MB";
        } else {
            return decimalFormat.format(size / (1024.0 * 1024 * 1024)) + "GB";
        }
    }

    /**
     * 将输入流内容转换为字符串
     *
     * @param inputStream 输入流对象，方法执行完毕后会自动关闭
     * @param charset     用于解码字节数据的字符集
     * @return 使用指定字符集解码后的字符串
     * @throws IOException 当发生I/O错误时抛出
     * 实现逻辑：
     * 1. 使用try-with-resources自动管理输入流
     * 2. 通过缓冲区循环读取输入流内容
     * 3. 将读取的字节数据转换为字符串
     */
    public static String readFileToString(InputStream inputStream, Charset charset) throws IOException {
        try (InputStream is = inputStream) {  // 使用 try-with-resources
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            byte[] buffer = new byte[4096];

            // 循环读取数据直到流结束
            int bytesRead;
            while ((bytesRead = is.read(buffer)) != -1) {
                outputStream.write(buffer, 0, bytesRead);
            }

            // 将缓存的字节数据转换为字符串
            return new String(outputStream.toByteArray(), charset);
        } // 自动关闭 InputStream
    }

    /**
     * 使用UTF-8编码将输入流内容转换为字符串
     *
     * @param inputStream 输入流对象，方法执行完毕后会自动关闭
     * @return 使用UTF-8字符集解码后的字符串
     * @throws IOException 当发生I/O错误时抛出
     */
    public static String readFileToString(InputStream inputStream) throws IOException {
        return readFileToString(inputStream, StandardCharsets.UTF_8);
    }

    /**
     * 读取文件内容为字符串（默认 UTF-8 编码）
     */
    public static String readFileToString(Path path) throws IOException {
        return readFileToString(path, StandardCharsets.UTF_8);
    }

    /**
     * 读取文件内容为字符串（支持自定义编码）
     */
    public static String readFileToString(Path path, Charset charset) throws IOException {
        byte[] bytes = Files.readAllBytes(path);
        return new String(bytes, charset);
    }

    /**
     * 复制文件
     * @param source 源文件
     * @param dest 目标文件
     * @throws IOException 如果复制过程中发生错误
     */
    private static void copyFile(File source, File dest) throws IOException {
        Files.copy(source.toPath(), dest.toPath());
    }

    /**
     * 获取MIME
     *
     * @param fileName 文件名
     */
    public static String mime(String fileName) {
        String tmp = mimeMap.getContentTypeFor(fileName);
        if (tmp == null) {
            return "application/octet-stream";
        } else {
            return tmp;
        }
    }

    /**
     * 移动文件
     *
     * @param source 源文件
     * @param dest 目标文件
     * @return 移动是否成功
     */
    public static boolean moveFile(File source, File dest) {
        // 如果源文件不存在，则抛出异常
        if (!source.exists()) {
            throw new IllegalArgumentException("源文件不存在");
        }
        // 如果目标文件已经存在，则抛出异常
        if (dest.exists()) {
            throw new IllegalArgumentException("目标文件已经存在");
        }
        try {
            // 复制文件
            copyFile(source, dest);
            // 删除源文件
            return source.delete();
        } catch (IOException e) {
            log.error("文件移动失败",e);
            return false;
        }
    }
}
